using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using System.IO;

namespace OPEN_ROBOT_GUI
{
    /// <summary>
    /// ABE GUI Form Class. Contains all the controls to manually operate an A.B.E. equipped robot.
    /// </summary>
    public partial class OPEN_ROBOT_ZIGBEE_GUI_Form : Form
    {
        // This delegate enables asynchronous calls for setting
        // the text property on a TextBox control.
        delegate void SetTextCallback(string text);

        // This delegate enables asynchronous calls for setting
        // the enabled property on a cmbo box control.
        delegate void SetcmbComPortEnable(bool bEnabled);

        // This delegate enables asynchronous calls for setting
        // the enabled property on a cmbo box control.
        delegate void SetcmbBaudRateEnable(bool bEnabled);

        /// <summary>
        /// Declare an instance of the ABEbot Functions Library.
        /// </summary>
        OPEN_ROBOT_ZigBee_Class_Library.OPEN_ROBOT_ZigBee_Functions OpenRobotLib = new OPEN_ROBOT_ZigBee_Class_Library.OPEN_ROBOT_ZigBee_Functions();

        /// <summary>
        /// Set the number of serial port read retries.
        /// </summary>
        int numRetries = 0;

        /// <summary>
        /// Application directory.
        /// </summary>
        string strApplicationDir = "";

        /// <summary>
        /// Port open thread. This thread will attempt to open the specified serial port.
        /// </summary>
        Thread trdOpenPort;

        /// <summary>
        /// COM Port name.
        /// </summary>
        string strComPort="COM1";

        /// <summary>
        /// Baud Rate for COM Port.
        /// </summary>
        Int32 intBaudRate=1200;

        /// <summary>
        /// Wheel Center Distance.
        /// </summary>
        double dblWheelCenterDistance = 4.375;

        /// <summary>
        /// Thread to read PIC18F4520 hex file and upload to robot.
        /// </summary>
        Thread hexDataReader;

        string picHexFileName, hexUploadError;

        /// <summary>
        /// ABE GUI Form Constructor.
        /// </summary>
        public OPEN_ROBOT_ZIGBEE_GUI_Form()
        {
            InitializeComponent();
        }

        /// <summary>
        /// Method to handle thread safe calls to the txtData textbox.
        /// </summary>
        /// <param name="text"></param>
        private void SetDataText(string text)
        {
            // InvokeRequired required compares the thread ID of the
            // calling thread to the thread ID of the creating thread.
            // If these threads are different, it returns true.
            if (this.txtData.InvokeRequired)
            {
                SetTextCallback d = new SetTextCallback(SetDataText);
                this.Invoke(d, new object[] { text });
            }
            else
            {
                this.txtData.Text = text;
            }
        }

        /// <summary>
        /// Method to handle thread safe calls to the lblPortLabel.
        /// </summary>
        /// <param name="text"></param>
        private void SetPortLabel(string text)
        {
            // InvokeRequired required compares the thread ID of the
            // calling thread to the thread ID of the creating thread.
            // If these threads are different, it returns true.
            if (this.lblPortLabel.InvokeRequired)
            {
                SetTextCallback d = new SetTextCallback(SetPortLabel);
                this.Invoke(d, new object[] { text });
            }
            else
            {
                this.lblPortLabel.Text = text;
            }
        }

        /// <summary>
        /// Method to handle thread safe calls to cmbComPort.Enabled
        /// </summary>
        /// <param name="bEnabled"></param>
        private void SetcmbComPortState(bool bEnabled)
        {
            // InvokeRequired required compares the thread ID of the
            // calling thread to the thread ID of the creating thread.
            // If these threads are different, it returns true.
            if (this.cmbComPort.InvokeRequired)
            {
                SetcmbComPortEnable d = new SetcmbComPortEnable(SetcmbComPortState);
                this.Invoke(d, new object[] { bEnabled });
            }
            else
            {
                this.cmbComPort.Enabled = bEnabled;
            }
        }

        /// <summary>
        /// Method to handle thread safe calls to cmbComPort.Enabled
        /// </summary>
        /// <param name="bEnabled"></param>
        private void SetcmbBaudRateState(bool bEnabled)
        {
            // InvokeRequired required compares the thread ID of the
            // calling thread to the thread ID of the creating thread.
            // If these threads are different, it returns true.
            if (this.cmbComPort.InvokeRequired)
            {
                SetcmbBaudRateEnable d = new SetcmbBaudRateEnable(SetcmbBaudRateState);
                this.Invoke(d, new object[] { bEnabled });
            }
            else
            {
                this.cmbBaudRate.Enabled = bEnabled;
            }
        }

        /// <summary>
        /// Thread function to try and open serial port.
        /// </summary>
        public void portOpenThreadStart()
        {
            Add_Message("Attempting to Open Serial Port.");
            string strResp = OpenRobotLib.Open_Serial_Port(strComPort, intBaudRate);
            if (strResp != "OK") { Add_Message(strResp); return; }

            portTimer.Enabled = false;
            SetPortLabel(strComPort + " Open");
            Add_Message("Serial Port is Open");
            //Disable both the cmbComPort and cmbBaudRate controls.
            SetcmbBaudRateState(false);
            SetcmbComPortState(false);
        }

        /// <summary>
        /// Function to close the serial port.
        /// </summary>
        public void Close_Serial_Port()
        {
            string strResp = OpenRobotLib.Close_Serial_Port();
            if (strResp != "OK") { Add_Message(strResp); return; }

            lblPortLabel.Text = "Select COM Port";
            cmbComPort.Enabled = true;
            cmbComPort.SelectedIndex = -1;
            cmbBaudRate.Enabled = true;
            cmbBaudRate.SelectedIndex = -1;
            Add_Message("Serial Port is Closed.");
        }

        /// <summary>
        /// Function to exit application.
        /// </summary>
        public void Exit_Application()
        {
            string strResp = OpenRobotLib.Close_Serial_Port();
            Application.Exit();
        }

        /// <summary>
        /// Open COM Port menu event.
        /// </summary>
        /// <param name="sender">object</param>
        /// <param name="e">EventArgs</param>
        private void mnuCOM_Open_Click(object sender, EventArgs e)
        {
            if ((cmbComPort.Text == "") || (cmbBaudRate.Text == ""))
            {
                MessageBox.Show("Select COM Port and Baud Rate!");
                return;
            }
            strComPort = cmbComPort.SelectedItem.ToString();
            intBaudRate = Convert.ToInt32(cmbBaudRate.SelectedItem);
            portTimer.Enabled = true;
            trdOpenPort = new Thread(new ThreadStart(portOpenThreadStart));
            trdOpenPort.Start();
        }

        /// <summary>
        /// Close COM Port menu event.
        /// </summary>
        /// <param name="sender">object</param>
        /// <param name="e">EventArgs</param>
        private void mnuCOM_Close_Click(object sender, EventArgs e)
        {
            Close_Serial_Port();
        }

        /// <summary>
        /// File Exit menu event.
        /// </summary>
        /// <param name="sender">object</param>
        /// <param name="e">EventArgs</param>
        private void mnuFile_Exit_Click(object sender, EventArgs e)
        {
            Exit_Application();
        }

        /// <summary>
        /// ABE GUI Form Load Event
        /// </summary>
        /// <param name="sender">object</param>
        /// <param name="e">EventArgs</param>
        private void ABE_GUI_Form_Load(object sender, EventArgs e)
        {
            string[] strPorts = OpenRobotLib.Get_Port_List();
            for (int i = 0; i < strPorts.Length; i++)
            {
                cmbComPort.Items.Add(strPorts[i]);
            }
            strApplicationDir = System.Environment.CurrentDirectory;

        }

        /// <summary>
        /// Send Motion Command Button Event.
        /// </summary>
        /// <param name="sender">object</param>
        /// <param name="e">EventArgs</param>
        private void bSendMotionCommand_Click(object sender, EventArgs e)
        {
            int leftMotor, rightMotor;

            try
            {
                leftMotor = Convert.ToInt16(nmbLeftMotor.Value);
                rightMotor = Convert.ToInt16(nmbRightMotor.Value);

                if (rdoOpenLoopVelocity.Checked)
                {
                    OpenRobotLib.SetOpenLoopVelocity(leftMotor, rightMotor, 500, numRetries);
                }
                else if (rdoClosedLoopVelocity.Checked)
                {
                    OpenRobotLib.SetClosedLoopVelocity(leftMotor, rightMotor, 500, numRetries);
                }
                else if (rdoPosition.Checked)
                {
                    OpenRobotLib.MoveToPosition(leftMotor, rightMotor, 500, numRetries);
                }
                else return;

                Add_Message("Move Command Recieved");
            }
            catch (Exception ex)
            {
                Add_Message(ex.Message);
            }
        }

        /// <summary>
        /// Stop Button Event.
        /// </summary>
        /// <param name="sender">object</param>
        /// <param name="e">EventArgs</param>
        private void bStop_Click(object sender, EventArgs e)
        {
            try
            {
                OpenRobotLib.StopRobot(500, numRetries);
                Add_Message("Robot Stopped");
            }
            catch (Exception ex)
            {
                Add_Message(ex.Message);
            } 
        }

        /// <summary>
        /// Is Move Complete Button Event.
        /// </summary>
        /// <param name="sender">object</param>
        /// <param name="e">EventArgs</param>
        private void bIsMoveComplete_Click(object sender, EventArgs e)
        {
            try
            {
                OpenRobotLib.IsMoveComplete(500, numRetries);
                Add_Message("Move Not Complete");
            }
            catch (Exception ex)
            {
                Add_Message(ex.Message);
            }            
        }

        /// <summary>
        /// Reset RFID Button Event.
        /// </summary>
        /// <param name="sender">object</param>
        /// <param name="e">EventArgs</param>
        private void bResetRFID_Click(object sender, EventArgs e)
        {
            try
            {
                OpenRobotLib.ResetRfidModule(500,numRetries);
                Add_Message("RFID Reset");
            }
            catch (Exception ex)
            {
                Add_Message(ex.Message);
            }
        }

        /// <summary>
        /// Write RFID Button Event.
        /// </summary>
        /// <param name="sender">object</param>
        /// <param name="e">EventArgs</param>
        private void bWriteRFID_Click(object sender, EventArgs e)
        {

            try
            {
                if (rdoRawMode.Checked) OpenRobotLib.WriteRfidDataASCII(txtRFIDascii.Text, 1500, numRetries);
                else if (rdoHexMode.Checked) OpenRobotLib.WriteRfidDataHex(txtRFIDhex.Text, 1500, numRetries);
                Add_Message("RFID Data Written");
            }
            catch (Exception ex)
            {
                Add_Message(ex.Message);
            }
        }

        /// <summary>
        /// Read RFID Button Event.
        /// </summary>
        /// <param name="sender">object</param>
        /// <param name="e">EventArgs</param>
        private void bReadRFID_Click(object sender, EventArgs e)
        {
            try
            {
                if (rdoRawMode.Checked) Add_Message("RFID ASCII = " + OpenRobotLib.GetAsciiRfidReading(1500,numRetries));
                else if (rdoHexMode.Checked) Add_Message("RFID HEX = " + OpenRobotLib.GetHexRfidReading(1500, numRetries));
            }
            catch (Exception ex)
            {
                Add_Message(ex.Message);
            }
        }

        /// <summary>
        /// Set Reflex Level Button Event.
        /// </summary>
        /// <param name="sender">object</param>
        /// <param name="e">EventArgs</param>
        private void bSetReflexLevel_Click(object sender, EventArgs e)
        {
            try
            {
                if (rdoADreflex.Checked) OpenRobotLib.SetAnalogToDigitalReflexLevel(Convert.ToInt16(nmbReflexLevel.Value), 500, numRetries);
                else OpenRobotLib.SetLightReflexLevel(Convert.ToInt16(nmbReflexLevel.Value), 500, numRetries);
                Add_Message("Reflex Set");
            }
            catch (Exception ex)
            {
                Add_Message(ex.Message);
            }
        }

        /// <summary>
        /// Get Reflex Level Button Event.
        /// </summary>
        /// <param name="sender">object</param>
        /// <param name="e">EventArgs</param>
        private void bGetReflexLevel_Click(object sender, EventArgs e)
        {
            try
            {
                byte[] robotData = OpenRobotLib.GetReflexLevels(500, numRetries);

                if (rdoADreflex.Checked)
                {
                    Add_Message("AD Reflex Level = " + robotData[0].ToString());
                    nmbReflexLevel.Value = robotData[0];
                }
                else
                {
                    Add_Message("Light Reflex Level = " + robotData[1].ToString());
                    nmbReflexLevel.Value = robotData[1];
                }
            }
            catch (Exception ex)
            {
                Add_Message(ex.Message);
            }
        }

        /// <summary>
        /// Reset Reflex Button Event.
        /// </summary>
        /// <param name="sender">object</param>
        /// <param name="e">EventArgs</param>
        private void bResetReflex_Click(object sender, EventArgs e)
        {
            try
            {
                OpenRobotLib.ResetSensorReflexTrigger(500, numRetries);
                Add_Message("Reflex Reset");
            }
            catch (Exception ex)
            {
                Add_Message(ex.Message);
            }

        }

        /// <summary>
        /// Is Reflex Triggered Button Event.
        /// </summary>
        /// <param name="sender">object</param>
        /// <param name="e">EventArgs</param>
        private void bReflexTriggered_Click(object sender, EventArgs e)
        {
            try
            {
                if (OpenRobotLib.IsReflexTriggered(500, numRetries)) Add_Message("Reflex Triggered");
                else Add_Message("Reflex Not Triggered");
            }
            catch (Exception ex)
            {
                Add_Message(ex.Message);
            }
        }

        /// <summary>
        /// Set Sensor Reflex Button Event.
        /// </summary>
        /// <param name="sender">object</param>
        /// <param name="e">EventArgs</param>
        private void bSetSensorReflex_Click(object sender, EventArgs e)
        {
            try
            {
                OpenRobotLib.SetSensorReflexes(txtSensorReflexes.Text, 500, numRetries);
                Add_Message("Sensor Reflexes Set");
            }
            catch (Exception ex)
            {
                Add_Message(ex.Message);
            }         
        }

        /// <summary>
        /// Get Wheel Encoder Readings Button Event.
        /// </summary>
        /// <param name="sender">object</param>
        /// <param name="e">EventArgs</param>
        private void bGetEncoders_Click(object sender, EventArgs e)
        {
            try
            {
                int[] robotData = OpenRobotLib.GetWheelEncoderReadings(500, numRetries);

                Add_Message("Right Encoder = " + robotData[1].ToString());
                Add_Message("Left Encoder = " + robotData[0].ToString());
            }
            catch (Exception ex)
            {
                Add_Message(ex.Message);
            }
        }

        /// <summary>
        /// Get Wheel Velocity Raedings Button Event.
        /// </summary>
        /// <param name="sender">object</param>
        /// <param name="e">EventArgs</param>
        private void bGetWheelVelocities_Click(object sender, EventArgs e)
        {
            try
            {
                int[] robotData = OpenRobotLib.GetWheelVelocities(500, numRetries);

                Add_Message("Right Wheel Velocity = " + robotData[1].ToString());
                Add_Message("Left Wheel Velocity = " + robotData[0].ToString());
            }
            catch (Exception ex)
            {
                Add_Message(ex.Message);
            }
        }

        /// <summary>
        /// Get AD Raedings Button Event.
        /// </summary>
        /// <param name="sender">object</param>
        /// <param name="e">EventArgs</param>
        private void bGetADReadings_Click(object sender, EventArgs e)
        {

            byte[] robotData = OpenRobotLib.GetAnalogToDigitalSensorReadings(500, numRetries);

            Add_Message("FRL = " + robotData[6].ToString());
            Add_Message("FLL = " + robotData[7].ToString());
            Add_Message("RRIR = " + robotData[5].ToString());
            Add_Message("RLIR = " + robotData[4].ToString());
            Add_Message("FRIR = " + robotData[3].ToString());
            Add_Message("FLIR = " + robotData[2].ToString());
            Add_Message("FCIR = " + robotData[1].ToString());
            Add_Message("Battery = " + System.String.Format("{0:#.00}", Convert.ToInt16(robotData[0]) * 37 / 688.5) + " Volts");
            Add_Message(robotData[0].ToString() + "," + robotData[1].ToString() + "," +
            robotData[2].ToString() + "," + robotData[3].ToString() + "," +
                robotData[4].ToString() + "," + robotData[5].ToString() + "," +
                robotData[6].ToString() + "," + robotData[7].ToString());
            if (robotData[0] < 130) Add_Message("Batteries are low and require charging or replacement!");
        }

        /// <summary>
        /// Set PID Gains Button Event.
        /// </summary>
        /// <param name="sender">object</param>
        /// <param name="e">EventArgs</param>
        private void bSetPIDgains_Click(object sender, EventArgs e)
        {
            int Kp, Ki, Kd;

            Kp = Convert.ToByte(nmbKp.Value);
            Ki = Convert.ToByte(nmbKi.Value);
            Kd = Convert.ToByte(nmbKd.Value);

            try
            {
                if (rdoPositionPID.Checked) OpenRobotLib.SetPositionGainsPID(Kp, Ki, Kd, 500, numRetries);
                else OpenRobotLib.SetVelocityGainsPID(Kp, Ki, Kd, 500, numRetries);
                Add_Message("PID Gains Set");
            }
            catch (Exception ex)
            {
                Add_Message(ex.Message);
            }
        }

        /// <summary>
        /// Get PID Gains Button Event.
        /// </summary>
        /// <param name="sender">object</param>
        /// <param name="e">EventArgs</param>
        private void bGetPIDgains_Click(object sender, EventArgs e)
        {
            try
            {
                byte[] robotData;

                if (rdoPositionPID.Checked)
                {
                    robotData = OpenRobotLib.GetPositionGainsPID(500, numRetries);
                }
                else
                {
                    robotData = OpenRobotLib.GetVelocityGainsPID(500, numRetries);
                }

                nmbKp.Value = robotData[0];
                nmbKi.Value = robotData[1];
                nmbKd.Value = robotData[2];

                Add_Message("Kd = " + nmbKd.Value.ToString());
                Add_Message("Ki = " + nmbKi.Value.ToString());
                Add_Message("Kp = " + nmbKp.Value.ToString());
            }
            catch (Exception ex)
            {
                Add_Message(ex.Message);
            }
        }

        /// <summary>
        /// Set Pin High Button Event.
        /// </summary>
        /// <param name="sender">object</param>
        /// <param name="e">EventArgs</param>
        private void bSetPinHigh_Click(object sender, EventArgs e)
        {
            int pinNum = Convert.ToByte(nmbPinNumber.Value);

            try
            {
                OpenRobotLib.SetDigitalPinState(pinNum, true, 500, numRetries);
                Add_Message("Pin Set High");
            }
            catch (Exception ex)
            {
                Add_Message(ex.Message);
            }
        }

        /// <summary>
        /// Set Pin Low Button Event.
        /// </summary>
        /// <param name="sender">object</param>
        /// <param name="e">EventArgs</param>
        private void bSetPinLow_Click(object sender, EventArgs e)
        {
            int pinNum = Convert.ToByte(nmbPinNumber.Value);

            try
            {
                OpenRobotLib.SetDigitalPinState(pinNum, false, 500, numRetries);
                Add_Message("Pin Set Low");
            }
            catch (Exception ex)
            {
                Add_Message(ex.Message);
            }
        }

        /// <summary>
        /// Get Pin Raeding Button Event.
        /// </summary>
        /// <param name="sender">object</param>
        /// <param name="e">EventArgs</param>
        private void bGetPinReading_Click(object sender, EventArgs e)
        {
            int pinNum = Convert.ToByte(nmbPinNumber.Value);

            try
            {
                bool robotData = OpenRobotLib.GetDigitalPinState(pinNum, 500, numRetries);

                if (robotData)
                    Add_Message("Pin State = High");
                else Add_Message("Pin State = Low");
            }
            catch (Exception ex)
            {
                Add_Message(ex.Message);
            }
        }

        /// <summary>
        /// Save To EEPROM Button Event.
        /// </summary>
        /// <param name="sender">object</param>
        /// <param name="e">EventArgs</param>
        private void bSaveToEEPROM_Click(object sender, EventArgs e)
        {
            try
            {
                OpenRobotLib.SaveParametersToEEPROM(500, numRetries);
                Add_Message("Data Saved");
            }
            catch (Exception ex)
            {
                Add_Message(ex.Message);
            }            
        }

        /// <summary>
        /// Clear Command Window Button Event.
        /// </summary>
        /// <param name="sender">object</param>
        /// <param name="e">EventArgs</param>
        private void bClearCommandWindow_Click(object sender, EventArgs e)
        {
            txtData.Text = "";
        }

        /// <summary>
        /// Number of retries numeric control value changed event.
        /// </summary>
        /// <param name="sender">object</param>
        /// <param name="e">EventArgs</param>
        private void nmbNumRetries_ValueChanged(object sender, EventArgs e)
        {
            numRetries = Convert.ToInt16(nmbNumRetries.Value);
        }

        /// <summary>
        /// Function to Add a Message to the Command Window.
        /// </summary>
        /// <param name="strMsg">Message string to add to Command Window.</param>
        private void Add_Message(string strMsg)
        {
            string strTmp = txtData.Text;
            SetDataText(strMsg + "\r\n" + strTmp);
        }

        /// <summary>
        /// Serial Port Open Watchdog Timer tick event.
        /// </summary>
        /// <param name="sender">object</param>
        /// <param name="e">EventArgs</param>
        private void portTimer_Tick(object sender, EventArgs e)
        {
            portTimer.Enabled = false;
            trdOpenPort.Abort();
            Close_Serial_Port();
            Add_Message("Error Opening Serial Port!");
        }

        /// <summary>
        /// Help Menu Manual Event Click Event.
        /// </summary>
        /// <param name="sender">object</param>
        /// <param name="e">EventArgs</param>
        private void mnuHelp_Manual_Click(object sender, EventArgs e)
        {
            string strFilePath = strApplicationDir + "\\" + "OPEN-ROBOT_Control_Center_Manual.pdf";
            try
            {
                System.Diagnostics.Process.Start(strFilePath);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message.ToString());
            }
        }

        /// <summary>
        /// Help Menu Online Support Click Event.
        /// </summary>
        /// <param name="sender">object</param>
        /// <param name="e">EventArgs</param>
        private void mnuHelp_OnlineSupport_Click(object sender, EventArgs e)
        {
            try
            {
                System.Diagnostics.Process.Start("www.abotics.com/abe_robot_controller.htm");
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message.ToString());
            }
        }

        /// <summary>
        /// Help Menu Email Support Click Event.
        /// </summary>
        /// <param name="sender">object</param>
        /// <param name="e">EventArgs</param>
        private void mnuEmailSupport_Click(object sender, EventArgs e)
        {
            try
            {
                System.Diagnostics.Process.Start("mailto:abe@abotics.com");
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message.ToString());
            }
        }

        private void bConvertToInches_Click(object sender, EventArgs e)
        {
            string strInches = String.Format("{0:#.00}", Convert.ToInt16(nmbTicks.Value) * 0.064);
            Add_Message("Ticks to Inches = " + strInches);
        }

        private void bConvertToDegrees_Click(object sender, EventArgs e)
        {
            string strDegrees = String.Format("{0:#.00}", Convert.ToInt16(nmbTicks.Value) * 2.621 * 2 * 180 / (dblWheelCenterDistance * 128));
            Add_Message("Ticks to Degrees = " + strDegrees);
        }

        /// <summary>
        /// Wander Mode Button Event.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void bWander_Click(object sender, EventArgs e)
        {
            try
            {
                OpenRobotLib.StartWanderMode(500, numRetries);
                Add_Message("Wander Mode");
            }
            catch (Exception ex)
            {
                Add_Message(ex.Message);
            }
        }

        /// <summary>
        /// Find Tag Event.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void bFindTag_Click(object sender, EventArgs e)
        {
            try
            {
                OpenRobotLib.FindRfidTag(500, numRetries);
                Add_Message("Searching for RFID Tags");
            }
            catch (Exception ex)
            {
                Add_Message(ex.Message);
            }             
        }

        /// <summary>
        /// Tag Found Event.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void bTagFound_Click(object sender, EventArgs e)
        {
            try
            {
                bool robotData = OpenRobotLib.IsRfidTagFound(500, numRetries);
                if (robotData) Add_Message("Tag Found");
                else Add_Message("No Tag Found");
            }
            catch (Exception ex)
            {
                Add_Message(ex.Message);
            }    
        }

        /// <summary>
        /// Stop Searching for RFID tags.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void bStopTagSearch_Click(object sender, EventArgs e)
        {
            try
            {
                OpenRobotLib.StopRfidTagSearch(500, numRetries);
                Add_Message("Tag Search Terminated.");
            }
            catch (Exception ex)
            {
                Add_Message(ex.Message);
            }
        }

        /// <summary>
        /// Thread to read and upload a new CCS C compiled hex file to OPEN-ROBOT's PIC18F4520.
        /// </summary>
        void ReadHexDataThread()
        {
            hexUploadError = "";

            if (File.Exists(picHexFileName) == false)
            {
                Add_Message("File Does Not Exist!");
                return;
            }

            StreamReader sr = new StreamReader(picHexFileName);

            string hexLine;
            string[] sendError;
            int lineNum = 0;
            bool readError = false;
            while (!sr.EndOfStream)
            {
                try
                {
                    hexLine = sr.ReadLine();
                    sendError = OpenRobotLib.Send_Data_Hex_Upload(hexLine,(char)17, 2000, numRetries);
                    if (sendError[0] != "OK")
                    {
                        hexUploadError = sendError[0];
                        sr.Close();
                        return;
                    }
                    char[] chrArray = sendError[1].ToCharArray();

                    if (hexLine[0] == ':')
                    {
                        // Verify returned chars.
                        if (Convert.ToByte(chrArray[0]) != 19) readError = true;
                        if (Convert.ToByte(chrArray[1]) != 6) readError = true;
                    }
                    else if (hexLine[0] == ';')
                    {
                        // Verify returned chars.
                        if (Convert.ToByte(chrArray[0]) != 6) readError = true;
                    }
                    // Return if a read error occurs.
                    if (readError == true)
                    {
                        hexUploadError = "Read Error! Hex file upload failed!";
                        sr.Close();
                        return;
                    }
                    lineNum++;
                    Add_Message("Line#" + lineNum.ToString() + " uploaded.");
                }
                catch (Exception ex)
                {
                    hexUploadError = ex.ToString();
                    sr.Close();
                    return;
                }
            }
            sr.Close();
        }

        /// <summary>
        /// File Upload Menu Click Event.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void mnuFileUploadPicHexFile_Click(object sender, EventArgs e)
        {
            if (openPicHexFile.ShowDialog() == DialogResult.Cancel) return;

            picHexFileName = openPicHexFile.FileName;

            hexDataReader = new Thread(new ThreadStart(ReadHexDataThread));

            hexDataReader.Start();
            hexThreadTimer.Enabled = true;
            hexThreadTimer.Start();
        }

        /// <summary>
        /// Hex Thread Timer.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void hexThreadTimer_Tick(object sender, EventArgs e)
        {
            hexThreadTimer.Stop();
            hexThreadTimer.Enabled = false;

            if (hexDataReader.IsAlive == false)
            {
                if (hexUploadError != "")
                {
                    Add_Message(hexUploadError);
                }
                else Add_Message("Hex file upload is complete.");
            }
            else
            {
                hexThreadTimer.Enabled = true;
                hexThreadTimer.Start();
            }
        }

        /// <summary>
        /// Get PWM Offset button press event.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void bGetPwmOffset_Click(object sender, EventArgs e)
        {

            try
            {
                byte[] robotData = OpenRobotLib.GetPwmOffsets(500, numRetries);
                Add_Message("PWM Motor Offsets = " + robotData[0].ToString() + "," + robotData[1].ToString());
            }
            catch (Exception ex)
            {
                Add_Message(ex.Message);
            }
        }

        /// <summary>
        /// Event to set left PWM motor offset.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void bSetLeftOffset_Click(object sender, EventArgs e)
        {
            try
            {
                OpenRobotLib.SetPwmOffset((int)numPwmOffset.Value, true, 500, numRetries);
                Add_Message("Left PWM Motor Offset is set");
            }
            catch (Exception ex)
            {
                Add_Message(ex.Message);
            }
        }

        /// <summary>
        /// Event to set right PWM motor offset.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void bSetRightOffset_Click(object sender, EventArgs e)
        {
            try
            {
                OpenRobotLib.SetPwmOffset((int)numPwmOffset.Value, false, 500, numRetries);
                Add_Message("Right PWM Motor Offset is set");
            }
            catch (Exception ex)
            {
                Add_Message(ex.Message);
            }
        }

        /// <summary>
        /// Event to get PIC18F4520 firmware version.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void mnuFileGetFirmwareVersion_Click(object sender, EventArgs e)
        {
            try
            {
                string robotData = OpenRobotLib.GetFirmwareVersion(500, numRetries);
                Add_Message(robotData);
            }
            catch (Exception ex)
            {
                Add_Message(ex.Message);
            }
        }
    }
}